using System;
using CLELCore.Assembly;

namespace CLELCore
{
	public static class Bits
	{
		public static byte ByteFromShort(int byte_index,short val)
		{
			byte ret = 0;
			if(byte_index == 0 || byte_index == 1)
			{
				ret = (byte)((val&(0xff<<(byte_index*8)))>>(byte_index*8));
			}
			return ret;
		}

		public static byte ByteFromUnsignedShort(int byte_index,ushort val)
		{
			return Bits.ByteFromShort(byte_index,(short)val);
		}

		public static byte ByteFromInt(int byte_index,int val)
		{
			byte ret = 0;
			if(byte_index >= 0 && byte_index <= 3)
			{
				ret = (byte)((val&(0xff<<(byte_index*8)))>>(byte_index*8));
			}
			return ret;
		}
		
		public static byte ByteFromFloat(int byte_index,float val)
		{
			byte ret = 0;
			if(byte_index >= 0 && byte_index <= 3)
			{
				byte [] bytes = BitConverter.GetBytes(val);
				return bytes[byte_index];
			}
			return ret;
			
		}
		
		public static byte ByteFromDouble(int byte_index,double val)
		{
			byte ret = 0;
			if(byte_index >= 0 && byte_index <= 7)
			{
				byte [] bytes = BitConverter.GetBytes(val);
				return bytes[byte_index];
			}
			return ret;
		}

		public static byte ByteFromLong(int byte_index,long val)
		{
			byte ret = 0;
			if(byte_index >= 0 && byte_index <= 7)
			{
				ret = (byte)((val&(0xff<<byte_index*8))>>(byte_index*8));
			}
			return ret;
		}

		public static int HighIntOfLong(long val)
		{
			return (int)((val>>32)&0xffffffff);
		}

		public static int LowIntOfLong(long val)
		{
			return (int)(val&0xffffffff);
		}

		public static long SetHighIntOfLong(long val,int high)
		{
			long v = 0xffffffff&val;
			return (high << 32) | v;
		}

		public static long SetLowIntOfLong(long val, int low)
		{
			long v = (0xffffffff<<32)&val;
			return low | v;
		}

		public static bool IsBitSet(int bit,long val)
		{
			if(bit >= 0 && bit < 64)
			{
				return (((val>>bit)&0x1) == 0x1);
			}
			return false;
		}

		public static bool IsBitSet(int bit,int val)
		{
			if(bit >= 0 && bit < 32)
			{
				return (((val>>bit)&0x1) == 0x1);
			}
			return false;
		}

		public static bool IsBitSet(int bit,short val)
		{
			if(bit >= 0 && bit < 16)
			{
				return (((val>>bit)&0x1) == 0x1);
			}
			return false;
		}
		
		public static bool IsBitSet(int bit,byte val)
		{
			if(bit >= 0 && bit < 8)
			{
				return (((val>>bit)&0x1) == 0x1);
			}
			return false;
		}

		public static int GetBits(int start,int end,int val)
		{
			if(start < 0 || end >= 32 || start > end)
			{
				return 0;
			}
			uint v = (uint)val;
			uint ret = v<<(31-end);
			ret >>= (31-end+start);
			//int mask = 0x1<<(end-start);
			//int mask = ~((~0x0)<<(7-(end-start)));
			//if(val == 0x81)
			//	Console.WriteLine(0x1<<30);
			return (int)ret;
		}

		public static int SetBits(int start,int end, int val)
		{
			if(start < 0 || end >= 32 || start > end)
			{
				return 0;
			}
			int ones = Bits.GetBits(start,end,~0xff);
			ones <<= start;
			return (val | ones);
		}

		public static int ClearBits(int start,int end,int val)
		{
			if(start < 0 || end >= 32 || start > end)
			{
				return 0;
			}
			int mask = ~(Bits.SetBits(start,end,0x0));
			return val&mask;
		}

		public static long MakeLong(byte val0,byte val1,byte val2,byte val3,
			byte val4,byte val5,byte val6,byte val7)
		{
			long high = (long)Bits.MakeInt(val4,val5,val6,val7);
			long low = (long)Bits.MakeInt(val0,val1,val2,val3);
			return (high<<32)|low;
		}

		public static uint MakeUnsignedInt(byte val0,byte val1,byte val2,byte val3)
		{
			return (uint)((val3<<24)|(val2<<16)|(val1<<8)|val0);
		}

		public static int MakeInt(byte val0,byte val1,byte val2,byte val3)
		{
			return (val3<<24)|(val2<<16)|(val1<<8)|val0;
		}

		public static short MakeShort(byte val0,byte val1)
		{
			return (short)((val1<<8)|val0);
		}
		
		public static ushort MakeUnsignedShort(byte val0,byte val1)
		{
			return (ushort)((val1<<8)|val0);
		}

		public static float MakeFloat(byte val0,byte val1,byte val2,byte val3)
		{
			byte [] val = new byte[4];
			val[0] = val0;
			val[1] = val1;
			val[2] = val2;
			val[3] = val3;
			return BitConverter.ToSingle(val,0);
		}
		
		public static double MakeDouble(byte val0,byte val1,byte val2,byte val3,byte val4,byte val5,byte val6,byte val7)
		{
			byte [] val = new byte[8];
			val[0] = val0;
			val[1] = val1;
			val[2] = val2;
			val[3] = val3;
			val[4] = val4;
			val[5] = val5;
			val[6] = val6;
			val[7] = val7;
			return BitConverter.ToDouble(val,0);
		}
		
		public static void SetLong(int off,byte[] buffer,long val,bool little_endian)
		{
			if(little_endian)
			{
				SetInt(off,buffer,LowIntOfLong(val),little_endian);
				SetInt(off+4,buffer,HighIntOfLong(val),little_endian);
			}
			else
			{
				SetInt(off,buffer,HighIntOfLong(val),little_endian);
				SetInt(off+4,buffer,LowIntOfLong(val),little_endian);
			}
		}
		
		public static void SetBytes(int off,byte[] buffer,byte[] val)
		{
			int i,stop = val.Length;
			for(i = 0;i < stop;i++)
				buffer[off+i] = val[i];
		}
		
		public static void SetShort(int off,byte[] buffer,short val,bool little_endian)
		{
			int i;
			if(little_endian)
				for(i = 0;i < 2;i++)
					buffer[off+i] = ByteFromShort(i,val);
			else
				for(i = 1;i >= 0;i--)
					buffer[off+i] = ByteFromShort(1-i,val);
		}
		
		public static void SetInt(int off,byte[] buffer,int val,bool little_endian)
		{
			int i;
			if(little_endian)
				for(i = 0;i < 4;i++)
					buffer[off+i] = ByteFromInt(i,val);
			else
				for(i = 3;i >= 0;i--)
					buffer[off+i] = ByteFromInt(3-i,val);
		}

		public static Token ReadToken(byte[] buffer,int off,bool little_endian)
		{
			int ret = ReadInt(buffer,off,little_endian);
			return new Token(ret);
		}

		public static int ReadInt(byte[] buffer,int off,bool little_endian)
		{
			if(little_endian)
				return Bits.MakeInt(buffer[off],buffer[off+1],buffer[off+2],buffer[off+3]);
			return Bits.MakeInt(buffer[off+3],buffer[off+2],buffer[off+1],buffer[off]);
		}

		public static short ReadShort(byte[] buffer,int off,bool little_endian)
		{
			if(little_endian)
				return Bits.MakeShort(buffer[off],buffer[off+1]);
			return Bits.MakeShort(buffer[off+1],buffer[off]);
		}

		public static ushort ReadUnsignedShort(byte[] buffer,int off,bool little_endian)
		{
			if(little_endian)
				return Bits.MakeUnsignedShort(buffer[off],buffer[off+1]);
			return Bits.MakeUnsignedShort(buffer[off+1],buffer[off]);
		}

		public static float ReadFloat(byte[] buffer,int off,bool little_endian)
		{
			if(little_endian)
				return Bits.MakeFloat(buffer[off],buffer[off+1],buffer[off+2],buffer[off+3]);
			return Bits.MakeFloat(buffer[off+3],buffer[off+2],buffer[off+1],buffer[off]);
		}

		public static double ReadDouble(byte[] buffer,int off,bool little_endian)
		{
			if(little_endian)
				return Bits.MakeDouble(buffer[off],buffer[off+1],buffer[off+2],buffer[off+3],buffer[off+4],buffer[off+5],buffer[off+6],buffer[off+7]);
			return Bits.MakeDouble(buffer[off+7],buffer[off+6],buffer[off+5],buffer[off+4],buffer[off+3],buffer[off+2],buffer[off+1],buffer[off]);
		}

		public static long ReadLong(byte[] buffer,int off,bool little_endian)
		{
			if(little_endian)
				return Bits.MakeLong(buffer[off],buffer[off+1],buffer[off+2],buffer[off+3],buffer[off+4],buffer[off+5],buffer[off+6],buffer[off+7]);
			return Bits.MakeLong(buffer[off+7],buffer[off+6],buffer[off+5],buffer[off+4],buffer[off+3],buffer[off+2],buffer[off+1],buffer[off]);
		}

		public static int AlignToFourByteBoundry(int offset)
		{
			int rem = offset % 4;
			if(rem != 0)
				return offset + (4-rem);
			return offset;
		}
	}
}
